跳到主要内容

Koa.js 中间件

中间件分类

狭义中间件

前言

狭义中间件的要素常见要素如下所示。

  • 一切皆中间件
  • 中间件内操作请求request
    • 请求拦截
  • 中间件内操作响应response
    • 响应拦截
  • 中间件内操作上下文context
    • 直接上下文代理,初始化实例时候挂载代理在app.context
    • 请求过程上下文代理,请求时候挂载代理在ctx
  • 大部分直接被app.use()加载
    • 注意: 初始化实例挂载代理context不被app.use()

请求拦截

const Koa = require('koa');
let app = new Koa();

const middleware = async function (ctx, next) {
// 中间件 拦截请求
// 把所有请求不是 /page/ 开头的路径全部抛出500错误
const reqPath = ctx.request.path;
if (reqPath.indexOf('/page/') !== 0) {
ctx.throw(500);
}
await next();
};

const page = async function (ctx, next) {
ctx.body = `
<html>
<head></head>
<body>
<h1>${ctx.request.path}</h1>
</body>
</html>
`;
};

app.use(middleware);
app.use(page);

app.listen(3001, function () {
console.log('the demo is start at port 3001');
});

响应拦截

const Koa = require('koa');
let app = new Koa();

const middleware = async function (ctx, next) {
ctx.response.type = 'text/plain';
await next();
};

const page = async function (ctx, next) {
ctx.body = `
<html>
<head></head>
<body>
<h1>${ctx.path}</h1>
</body>
</html>
`;
};

app.use(middleware);
app.use(page);

app.listen(3001, function () {
console.log('the demo is start at port 3001');
});

context挂载代理

  • 请求代理注入
    • 直接被app.use
    • 请求时候才有注入
    • 每次请求的注入都不同
  • 初始化实例(应用)代理注入
    • 直接注入到app.context
    • 初始化应用的时候才注入
    • 只注入一次,每次请求都可以使用

请求时挂载代理context

const Koa = require('koa');
let app = new Koa();

const middleware = async function (ctx, next) {
// 中间件 代理/挂载上下文
// 把所有当前服务的进程PID,内存使用情况方法代理/挂载在ctx上
ctx.getServerInfo = function () {
function parseMem(mem = 0) {
let memVal = mem / 1024 / 1024;
memVal = memVal.toFixed(2) + 'MB';
return memVal;
}

function getMemInfo() {
let memUsage = process.memoryUsage();
let rss = parseMem(memUsage.rss);
let heapTotal = parseMem(memUsage.heapTotal);
let heapUsed = parseMem(memUsage.heapUsed);
return {
pid: process.pid,
rss,
heapTotal,
heapUsed
};
}
return getMemInfo();
};
await next();
};

const page = async function (ctx, next) {
const serverInfo = ctx.getServerInfo();
ctx.body = `
<html>
<head></head>
<body>
<p>${JSON.stringify(serverInfo)}</p>
</body>
</html>
`;
};

app.use(middleware);
app.use(page);

app.listen(3001, function () {
console.log('the demo is start at port 3001');
});

初始化实例挂载代理context

const Koa = require('koa');
let app = new Koa();

const middleware = function (app) {
// 中间件在初始化实例 把getServerInfo方法 挂载代理到上下文
app.context.getServerInfo = function () {
function parseMem(mem = 0) {
let memVal = mem / 1024 / 1024;
memVal = memVal.toFixed(2) + 'MB';
return memVal;
}

function getMemInfo() {
let memUsage = process.memoryUsage();
let rss = parseMem(memUsage.rss);
let heapTotal = parseMem(memUsage.heapTotal);
let heapUsed = parseMem(memUsage.heapUsed);
return {
pid: process.pid,
rss,
heapTotal,
heapUsed
};
}
return getMemInfo();
};
};

middleware(app);

const page = async function (ctx, next) {
const serverInfo = ctx.getServerInfo();
ctx.body = `
<html>
<head></head>
<body>
<p>${JSON.stringify(serverInfo)}</p>
</body>
</html>
`;
};

app.use(page);

app.listen(3001, function () {
console.log('the demo is start at port 3001');
});

广义中间件

前言

  • 不直接提供中间件
  • 通过间接方式提供了中间件,最常见的是间接中间件子中间件
  • 间接被 app.use() 加载
  • 其他方式接入Koa切面

间接中间件

const Koa = require('koa');
let app = new Koa();

function indirectMiddleware(path, middleware) {
return async function (ctx, next) {
console.log(ctx.path === path, middleware);
if (ctx.path === path) {
await middleware(ctx, next);
} else {
await next();
}
};
}

const index = async function (ctx, next) {
ctx.body = 'this is index page';
};

const hello = async function (ctx, next) {
ctx.body = 'this is hello page';
};

const world = async function (ctx, next) {
ctx.body = 'this is world page';
};

app.use(indirectMiddleware('/', index));
app.use(indirectMiddleware('/hello', hello));
app.use(indirectMiddleware('/world', world));

app.listen(3001, () => {
console.log('the demo is start at port 3001');
});

子中间件

子中间件是广义中间件的一个最有代表场景,主要的特点有

  • 初始化中间件时,内置子中间件列表
  • 子中间件列表添加子中间件元素
  • 子中间件列表封装成间接中间件,让后被app.use()加载
const Koa = require('koa');
let app = new Koa();

class Middleware {
constructor() {
this.stack = [];
}

get(path, childMiddleware) {
this.stack.push({ path, middleware: childMiddleware });
}

middlewares() {
let stack = this.stack;
return async function (ctx, next) {
let path = ctx.path;
for (let i = 0; i < stack.length; i++) {
const child = stack[i];
if (child && child.path === path && child.middleware) {
await child.middleware(ctx, next);
}
}
await next();
};
}
}

const middleware = new Middleware();

middleware.get('/page/001', async (ctx, next) => {
ctx.body = 'page 001';
});
middleware.get('/page/002', async (ctx, next) => {
ctx.body = 'page 002';
});
middleware.get('/page/003', async (ctx, next) => {
ctx.body = 'page 003';
});

app.use(middleware.middlewares());

app.listen(3001, function () {
console.log('the demo is start at port 3001');
});